<?php

abstract class AbstractPage
{
	/**
	 * constants
	 */
	const ROOT_ID = 0;
	const RELOAD = true;
	
	/**
	 * static variables
	 */
	private static $instance;
	
	/**
	 * instance variables
	 */
	private $pageList;
	private $currentPage;
	private $currentPost;
	private $currentCategory;
	
	// lazy-loaded
	private $firstLevelPageIdList;
	private $firstLevelPageList;
	private $secondLevelPageIdList;
	private $secondLevelPageList;
	private $thirdLevelPageIdList;
	private $thirdLevelPageList;
	private $formattedPageList;
	private $currentPageTree;
	private $currentPageLevel;
	
	/**
	 * static methods
	 */
	public static function getInstance()
	{
		if (is_null(self::$instance))
		{
			self::$instance = new PronexusPage();
		}
		
		return self::$instance;
	}
	
	/**
	 * instance methods
	 */
	protected function __construct()
	{
		$this -> pageList = null;
		$this -> currentPage = null;
		$this -> currentPost = null;
		$this -> currentCategory = null;
		
		// lazy-loaded
		$this -> formattedPageList = null;
		$this -> firstLevelPageIdList = null;
		$this -> firstLevelPageList = null;
		$this -> secondLevelPageIdList = null;
		$this -> secondLevelPageList = null;
		$this -> thirdLevelPageIdList = null;
		$this -> thirdLevelPageList = null;
		$this -> currentPageTree = null;
		$this -> currentPageLevel = null;
	}
	
	private function __clone()
	{
		
	}
	
	public function setPageList(array $pageList)
	{
		$this -> pageList = $pageList;
		
		return $this;
	}
	
	/**
	 * @see setPageList()
	 */
	public function getPageList()
	{
		if (is_null($this -> pageList))
		{
			throw new Exception("Page list has not been set");
		}
		
		return $this -> pageList;
	}
	
	public function getPageFromPageId($pageId)
	{
		foreach ($this -> getPageList() as $page)
		{
			if ($page -> ID == $pageId)
			{
				return $page;
			}
		}
		
		return null;
	}
	
	public function getPageListFromPageIdList(array $pageIdList)
	{
		return array_map(array($this, "getPageFromPageId"), $pageIdList);
	}
	
	/**
	 * @see initialisePageList()
	 */
	public function getFormattedPageList($reload = false)
	{
		if ($reload || is_null($this -> formattedPageList))
		{
			$pageList = $this -> getPageList();
			
			$firstLevelPageIdList = array();
			$firstLevelPageList = array();
			$secondLevelPageIdList = array();
			$secondLevelPageList = array();
			$thirdLevelPageIdList = array();
			$thirdLevelPageList = array();
			
			foreach ($pageList as $page)
			{
				$pageParentId = $page -> post_parent;
				$pageId = $page -> ID;
				
				if ($pageParentId == self::ROOT_ID && !in_array($pageId, $firstLevelPageIdList))
				{
					$firstLevelPageIdList[] = $pageId;
					$firstLevelPageList[] = $page;
					$formattedPageList[$pageId] = array("title" => $page -> post_title, "children" => array());
				}
				else if (in_array($pageParentId, $firstLevelPageIdList) && !in_array($pageId, $secondLevelPageIdList))
				{
					$secondLevelPageIdList[] = $pageId;
					$secondLevelPageList[] = $page;
					$formattedPageList[$pageParentId]["children"][$pageId] = array("title" => $page -> post_title, "children" => array());
				}
				else if (in_array($pageParentId, $secondLevelPageIdList) && !in_array($pageId, $thirdLevelPageIdList))
				{
					$thirdLevelPageIdList[] = $pageId;
					$thirdLevelPageList[] = $page;
					foreach ($formattedPageList as $firstLevelPageId => $pageData)
					{
						if (in_array($pageParentId, array_keys($pageData["children"])))
						{
							$formattedPageList[$firstLevelPageId]["children"][$pageParentId]["children"][$pageId] = array("title" => $page -> post_title, "children" => array());
							
							break;
						}
					}
				}
			}
			
			$this -> firstLevelPageIdList = $firstLevelPageIdList;
			$this -> firstLevelPageList = $firstLevelPageList;
			$this -> secondLevelPageIdList = $secondLevelPageIdList;
			$this -> secondLevelPageList = $secondLevelPageList;
			$this -> thirdLevelPageIdList = $thirdLevelPageIdList;
			$this -> thirdLevelPageList = $thirdLevelPageList;
			$this -> formattedPageList = $formattedPageList;
		}
		
		return $this -> formattedPageList;
	}
	
	/**
	 * @see initialisePageList()
	 */
	public function getFirstLevelPageIdList()
	{
		if (is_null($this -> firstLevelPageIdList))
		{
			throw new Exception("First-level Page ID list not set");
		}
		
		return $this -> firstLevelPageIdList;
	}
	
	/**
	 * @see initialisePageList()
	 */
	public function getFirstLevelPageList()
	{
		if (is_null($this -> firstLevelPageList))
		{
			throw new Exception("First-level Page list not set");
		}
		
		return $this -> firstLevelPageList;
	}
	
	/**
	 * @see initialisePageList()
	 */
	public function getSecondLevelPageIdList()
	{
		if (is_null($this -> secondLevelPageIdList))
		{
			throw new Exception("Second-level Page ID list not set");
		}
		
		return $this -> secondLevelPageIdList;
	}
	
	/**
	 * @see initialisePageList()
	 */
	public function getSecondLevelPageList()
	{
		if (is_null($this -> secondLevelPageList))
		{
			throw new Exception("Second-level Page list not set");
		}
		
		return $this -> secondLevelPageList;
	}
	
	/**
	 * @see initialisePageList()
	 */
	public function getThirdLevelPageIdList()
	{
		if (is_null($this -> thirdLevelPageIdList))
		{
			throw new Exception("Third-level Page ID list not set");
		}
		
		return $this -> thirdLevelPageIdList;
	}
	
	/**
	 * @see initialisePageList()
	 */
	public function getThirdLevelPageList()
	{
		if (is_null($this -> thirdLevelPageList))
		{
			throw new Exception("Third-level Page list not set");
		}
		
		return $this -> thirdLevelPageList;
	}
	
	public function setCurrentPage($page)
	{
		$this -> currentPage = $page;
		
		return $this;
	}
	
	/**
	 * @see setCurrentPage()
	 */
	public function getCurrentPage()
	{
		if (!$this -> hasCurrentPage())
		{
			throw new Exception("Current page has not been set");
		}
		
		return $this -> currentPage;
	}
	
	public function hasCurrentPage()
	{
		return !is_null($this -> currentPage);
	}
	
	protected function currentPageHasPageId($pageId)
	{
		return $this -> getCurrentPage() -> ID == $pageId;
	}
	
	public function setCurrentCategory($category)
	{
		$this -> currentCategory = $category;
		
		return $this;
	}
	
	/**
	 * @see setCurrentCategory()
	 */
	public function getCurrentCategory()
	{
		if (is_null($this -> currentCategory))
		{
			throw new Exception("Current category has not been set");
		}
		
		return $this -> currentCategory;
	}
	
	public function setCurrentPost($post)
	{
		$this -> currentPost = $post;
		
		return $this;
	}
	
	public function getCurrentPost()
	{
		if (!$this -> hasCurrentPost())
		{
			throw new Exception("Current post has not been set");
		}
		
		return $this -> currentPost;
	}
	
	public function hasCurrentPost()
	{
		return !is_null($this -> currentPost);
	}
	
	public function currentCategoryHasCategoryId($categoryId)
	{
		return $this -> getCurrentCategory() -> cat_ID == $categoryId;
	}
	
	/**
	 * @see intialisePageList()
	 */
	public function getCurrentPageTree($reload = false)
	{
		if (!$reload && !is_null($this -> currentPageTree))
		{
			return $this -> currentPageTree;
		}
		else
		{
			$currentPage = $this -> getCurrentPage();
			$formattedPageList = $this -> getFormattedPageList();
			if ($this -> isFirstLevelPage())
			{
				$this -> currentPageTree = $formattedPageList[$currentPage -> ID];
				$this -> currentPageTree["id"] = $currentPage -> ID;
			}
			else if ($this -> isSecondLevelPage())
			{
				foreach ($formattedPageList as $firstLevelPageId => $firstLevelPageData)
				{
					if (in_array($currentPage -> ID, array_keys($firstLevelPageData["children"])))
					{
						$this -> currentPageTree = $formattedPageList[$firstLevelPageId];
						$this -> currentPageTree["id"] = $firstLevelPageId;
					}
				}
			}
			else if ($this -> isThirdLevelPage())
			{
				foreach ($formattedPageList as $firstLevelPageId => $firstLevelPageData)
				{
					foreach ($firstLevelPageData["children"] as $secondLevelPageId => $secondLevelPageData)
					{
						if (in_array($currentPage -> ID, array_keys($secondLevelPageData["children"])))
						{
							$this -> currentPageTree = $formattedPageList[$firstLevelPageId];
							$this -> currentPageTree["id"] = $firstLevelPageId;
						}
					}
				}
			}
			return $this -> currentPageTree;
		}
		
		return null;
	}
	
	public function isFirstLevelPage()
	{
		return in_array($this -> getCurrentPage() -> ID, $this -> getFirstLevelPageIdList());
	}
	
	public function isSecondLevelPage()
	{
		return in_array($this -> getCurrentPage() -> ID, $this -> getSecondLevelPageIdList());
	}
	
	public function isThirdLevelPage()
	{
		return in_array($this -> getCurrentPage() -> ID, $this -> getThirdLevelPageIdList());
	}
	
	/**
	 * this is buggy and i do not recommend using this
	 * 
	 * @see isFirstLevelPage()
	 * @see isSecondLevelPage()
	 * @see isThirdLevelPage()
	 */
	public function getCurrentPageLevel($reload = false)
	{
		if (!$reload && !is_null($this -> currentPageLevel))
		{
			return $this -> currentPageLevel;
		}
		else
		{
			$pageLevel = -1;
			$currentPage = $this -> getCurrentPage();
			
			if (in_array($currentPage -> ID, $this -> getFirstLevelPageIdList()))
			{
				$pageLevel = 1;
			}
			else if (in_array($currentPage -> ID, $this -> getSecondLevelPageIdList()))
			{
				$pageLevel = 2;
			}
			else if (in_array($currentPage -> ID, $this -> getThirdLevelPageIdList()))
			{
				$pageLevel = 3;
			}
			
			return $pageLevel;
		}
	}
	
	public function exception_handler(Exception $e)
	{
		printf(
			'<pre style="background-color: #fff; margin: 1em; padding: 1em; border: 1px solid #000;">%s</pre>',
			sprintf(
				"Uncaught exception!\n"
				. "\tClass-name: %s\n"
				. "\tMessage: %s\n"
				. "\tFile: %s\n"
				. "\tLine: %s\n"
				. "\tBacktrace\n\t\t%s"
				,
				get_class($e),
				$e -> getMessage(),
				$e -> getFile(),
				$e -> getLine(),
				str_replace("\n", "\n\t\t", $e -> getTraceAsString())
			)
		);
	}
}